#include "drv_adc.h"
#include "public_datas.h"
#include "public_apis.h"

#define TAG "ADC"

typedef void (*adc_conver_done_cb_t)(int8_t temp, uint8_t soc, uint16_t volt);

static int16_t RoughCalib_Value = 0;
static uint8_t busy = 0;

static adc_conver_done_cb_t adc_conver_done_cb = NULL;

static void drv_adc_update_temp_soc();

static void drv_adc_register_conver_done_cb(void *arg, size_t len)
{
    adc_conver_done_cb = (adc_conver_done_cb_t)arg;
}

static void update_cb(void *arg, size_t len)
{
    drv_adc_update_temp_soc();
}

void drv_adc_install()
{
    gpio_set_mode(ADC_EN, GPIO_MODE_IN_PULLDOWN);
    gpio_set_mode(ADC_BAT, GPIO_MODE_IN_FLOAT);
    ADC_ExtSingleChSampInit(SampleFreq_3_2, ADC_PGA_1_2);
    RoughCalib_Value = ADC_DataCalib_Rough();
    topic_messenger_subscribe(NULL, "wakeup", update_cb);
    topic_messenger_subscribe(NULL, "adc_update", update_cb);
    topic_messenger_subscribe(NULL, "adc_set_done_cb", drv_adc_register_conver_done_cb);
}

static void get_temp_soc(void *arg)
{
    int32_t sum = 0;
    ADC_InterTSSampInit();
    int32_t var = ADC_ExcutSingleConver();
    public_datas()->temp = adc_to_temperature_celsius(var);
    ADC_DisableTSPower();

    ADC_ChannelCfg(ADC_BAT_CH);
    ADC_ExtSingleChSampInit(SampleFreq_3_2, ADC_PGA_1_2);
    for (size_t i = 0; i < BAT_SAMPLE_CNT; i++)
    {
        sum += ADC_ExcutSingleConver();
    }
    var = sum / BAT_SAMPLE_CNT + RoughCalib_Value;
    ADC_DisablePower();

    gpio_set_mode(ADC_EN, GPIO_MODE_IN_PULLDOWN);

    var = var * 1050L / 1024L - 1050L;
    var = var * 4 / 3;
    public_datas()->bat_volt = var;

    int8_t soc = 0;
    if (var > 3900)
    {
        soc = (var - 3900) / 30 + 95;
    }
    else if (var > 3700)
    {
        soc = (var - 3700) * 75 / 200 + 20;
    }
    else
    {
        soc = (var - 3000) / 35;
    }

    if (soc > 100)
        soc = 100;
    if (soc < 0)
        soc = 0;

    if (soc == public_datas()->bat_soc)
        goto out;

    if (public_datas()->usb_sta == ENABLE)
    {
        if (soc > public_datas()->bat_soc || public_datas()->bat_soc == 255)
            public_datas()->bat_soc = soc;
    }
    else if (soc < public_datas()->bat_soc)
    {
        public_datas()->bat_soc = soc;
    }

out:

    if (adc_conver_done_cb)
        adc_conver_done_cb(public_datas()->temp, public_datas()->bat_soc, public_datas()->bat_volt);

    busy = 0;
}

static void drv_adc_update_temp_soc()
{
    if (busy)
        return;
    busy = 1;
    gpio_set_mode(ADC_EN, GPIO_MODE_OUT_PUSHPULL_5mA);
    gpio_set_level(ADC_EN, 1);
    public_task_start(get_temp_soc, NULL, 5);
}